#include "edge.h"

edgeList CreateEmptyEdgeList(){
    edgeList E;
	FIRST(E) = NULL;
	return E;
}


edgeAddress	AllocateEdge(nodeAddress A, nodeAddress B, float uploadSpeed, float downloadSpeed){
    edgeAddress E = (edgeAddress) malloc(sizeof(Edge));
    if (E==NULL)
       return NULL;
    else {
       GetA(E)=A;
       GetB(E)=B;
       GetUpload(E)=uploadSpeed;
       GetDownload(E)=downloadSpeed;
       NEXT(E)=NULL;  
       return E;
    }
}


edgeAddress	AllocateEdgeAdv(nodeAddress A, nodeAddress B, float uploadSpeed, float downloadSpeed, edgeAddress next){
    edgeAddress E=AllocateEdge(A,B,uploadSpeed,downloadSpeed);
    if(E) NEXT(E)=next;
    return E;        
}

edgeAddress SearchEdgeByNode(edgeList *EL, nodeAddress node) {
	if(ISLINKEDLISTEMPTY(*EL)) return NULL;
	else if(GetA(FIRST(*EL))==node || GetB(FIRST(*EL))==node) {
		return FIRST(*EL);
	}
	else {
		edgeAddress E;
		TRAVERSETOEND(E,FIRST(*EL))
			if(GetA(NEXT(E))==node || GetB(NEXT(E))==node) break;
		
		return NEXT(E);
	}
}

edgeAddress SearchEdgeByNodes(edgeList *EL, nodeAddress A, nodeAddress B) {
	if(ISLINKEDLISTEMPTY(*EL)) return NULL;
	else if((GetA(FIRST(*EL))==A || GetB(FIRST(*EL))==A) && (GetA(FIRST(*EL))==B || GetB(FIRST(*EL))==B)) {
		return FIRST(*EL);
	}
	else {
		edgeAddress E;
		TRAVERSETOEND(E,FIRST(*EL))
			if((GetA(FIRST(*EL))==A || GetB(FIRST(*EL))==A) && (GetA(FIRST(*EL))==B || GetB(FIRST(*EL))==B)) break;
		
		return NEXT(E);
	}
}

bool		SearchEdge(edgeList *EL, edgeAddress E) {
	if(ISLINKEDLISTEMPTY(*EL)) return false;
	else {
		edgeAddress i;
		TRAVERSETOEND(i,FIRST(*EL))
			if(i==E) break;
		
		return i==E;
	}
}

void		DestroyEdge(edgeList *EL, edgeAddress E) {
	if(FIRST(*EL)==E) {
		FIRST(*EL) = NEXT(FIRST(*EL));
	}
	else {
		edgeAddress i;
		TRAVERSETOEND(i,FIRST(*EL)) {
			if(NEXT(i)==E) break;
		}
		
		NEXT(i) = NEXT(E);
	}
	
	DeallocateEdge(E);
}

void		DestroyEdgeList(edgeList *EL) {
	edgeAddress E;
	while(!ISLINKEDLISTEMPTY(*EL)) {
		E = FIRST(*EL);
		FIRST(*EL) = NEXT(E);
		DeallocateEdge(E);
	}
}

int EdgeLess(edgeAddress E, edgeAddress e){
    bool retval = (IsStringLt(NODENAME(GetA(E)),NODENAME(GetA(e))));
    if(!retval) retval = (IsStringEqual(NODENAME(GetA(E)),NODENAME(GetA(e)))) && (IsStringLt(NODENAME(GetB(E)),NODENAME(GetB(e))));
    if(!retval) retval = (IsStringEqual(NODENAME(GetA(E)),NODENAME(GetA(e)))) && (IsStringEqual(NODENAME(GetB(E)),NODENAME(GetB(e)))) && GetDownload(E) < GetDownload(e);
    if(!retval) retval = (IsStringEqual(NODENAME(GetA(E)),NODENAME(GetA(e)))) && (IsStringEqual(NODENAME(GetB(E)),NODENAME(GetB(e)))) 
							&& (GetDownload(E)==GetDownload(E)) && GetUpload(E) < GetUpload(e); 
    return retval;
}

void  AddEdge(edgeList *L, edgeAddress e){
        edgeAddress i;
        int stop;
    if (FIRST(*L)==NULL || EdgeLess(e,FIRST(*L))) {
		NEXT(e) = FIRST(*L);
		FIRST(*L) = e;
    }
    else {
        TRAVERSETOEND(i,FIRST(*L))
			if(EdgeLess(e,NEXT(i))) break;
                        
		NEXT(e) = NEXT(i);
		NEXT(i) = e;
    }
}

void  DeallocateEdge(edge *e){
    free(e);   
}

void GraphShortestPath(int size, int type, edgeList Graph, nodeAddress from, nodeAddress to, float *out_time, char *out_shortestpath){
   
   edgeAddress Next=Graph;      // Dipakai untuk pengecekan pada Adjacency List
   char temp_path[100];         // Temporary string path
   char temp_path_next[100];    // Temporary string path untuk rekursif selanjutnya
   float temp_time=0;           // Temporary waktu terhitung
   float temp_time_next;        // Temporary waktu untuk rekursif selanjutnya
   ownerList Out=CreateEmptyOwnerList();    // Membuat List Node yang terlewati
   AddOwner(&Out, AllocateOwnerList(from)); // Menambahkan Node from kepada List Node yang terlewati
   *out_time=MAXTIME;                       // Initial value untuk total waktu
   CopyStr(out_shortestpath,"FALSE");       // Initial value untuk string path
   CopyStr(temp_path,NODENAME(from).s);     // Menambahkan nama Node from kepada string path 
   
    
   if ( from==to){            // Jika from==to, atau node akhir telah ditemukan
       *out_time=0;
        CopyStr(out_shortestpath,NODENAME(to).s);
       if (type==DOWNLOAD)
          (*out_time)+=RETRIEVETIME(to);
       if (type==UPLOAD) 
           (*out_time)+=INSERTTIME(to);  
   }
   else while (Next!=NULL) {  // Pengecekan List adjacency pertama
       if   ( Next->A==from){
            if (SearchOwnerByNode(&Out,Next->B)==NULL){
                  CopyStr(temp_path_next,temp_path);
                  AddStr(temp_path_next,"->");
                  AddStr(temp_path_next,NODENAME(Next->B).s);
                 temp_time_next=temp_time;
                 if (type==DOWNLOAD) {
                    if (Next->downloadSpeed>0)
                       GraphShortestPathRecursive(size,type,Graph,Next->B,to,out_time,out_shortestpath,temp_time_next+size/(1024*Next->downloadSpeed),temp_path_next,Out);
                 }
                 if (type==UPLOAD) {
                    if (Next->uploadSpeed>0)
                       GraphShortestPathRecursive(size,type,Graph,Next->B,to,out_time,out_shortestpath,temp_time_next+size/(1024*Next->uploadSpeed),temp_path_next,Out);  
                 }
            }
       }
       Next=Next->next;
   }
   Next=Graph;  // inisialisasi pengecekan list adjacency
    while (Next!=NULL) {         // pengecekan list adjacency kedua
       if   ( Next->B==from){
            if (SearchOwnerByNode(&Out,Next->A)==NULL){
                  CopyStr(temp_path_next,temp_path);
                  AddStr(temp_path_next,"->");
                  AddStr(temp_path_next,NODENAME(Next->A).s);
                 temp_time_next=temp_time;
                 if (type==DOWNLOAD) {
                    if (Next->downloadSpeed>0) 
                       GraphShortestPathRecursive(size,type,Graph,Next->A,to,out_time,out_shortestpath,temp_time_next+size/(1024*Next->downloadSpeed),temp_path_next,Out);
                 }
                 if (type==UPLOAD) {
                    if (Next->uploadSpeed>0)
                    GraphShortestPathRecursive(size,type,Graph,Next->A,to,out_time,out_shortestpath,temp_time_next+size/(1024*Next->uploadSpeed),temp_path_next,Out);  
                 }
            }
       }
       Next=Next->next;
   }
   if (SameStr(out_shortestpath,"FALSE"))  // Untuk safety, jika string tetap FALSE, out_time juga harus tetap menjadi MAXTIME
      *out_time=MAXTIME;
   if (*out_time==MAXTIME)                 // Untuk safety juga
      CopyStr(out_shortestpath,"FALSE");
}

void GraphShortestPathRecursive(int size, int type, edgeList Graph, nodeAddress from, nodeAddress to, float *out_time, char *out_shortestpath, float temp_time, char *temp_path_get, ownerList Own){
   char temp_path[100];
   CopyStr(temp_path,temp_path_get);
   char temp_path_next[100];
   float temp_time_next;
   edgeList Next=Graph;
   ownerList Out= CreateEmptyOwnerList();
   Out= CopyOwnerList(Own);
   AddOwner(&Out, AllocateOwnerList(from));
   if ( from==to){
       temp_time_next=temp_time;
       if (type==DOWNLOAD)
          (temp_time_next)+=RETRIEVETIME(to);
       if (type==UPLOAD) 
           (temp_time_next)+=INSERTTIME(to);
       if ((*out_time>temp_time_next)||(*out_time==-1)){
            CopyStr(out_shortestpath,temp_path); 
           *out_time=temp_time_next;                       
       }
   }
   else while (Next!=NULL) {
       if   ( Next->A==from){
            if (SearchOwnerByNode(&Out,Next->B)==NULL){
                  CopyStr(temp_path_next,temp_path);
                  AddStr(temp_path_next,"->");
                  AddStr(temp_path_next,NODENAME(Next->B).s);
                 temp_time_next=temp_time;
                 if (type==DOWNLOAD)
                   if (Next->downloadSpeed>0)
                      GraphShortestPathRecursive(size,type,Graph,Next->B,to,out_time,out_shortestpath,temp_time_next+size/(1024*Next->downloadSpeed),temp_path_next,Out);
                 if (type==UPLOAD)
                   if (Next->uploadSpeed>0)
                      GraphShortestPathRecursive(size,type,Graph,Next->B,to,out_time,out_shortestpath,temp_time_next+size/(1024*Next->uploadSpeed),temp_path_next,Out);
            }
       }
       Next=Next->next;
   }
   Next=Graph;
   while (Next!=NULL) {
       if   ( Next->B==from){
            if (SearchOwnerByNode(&Out,Next->A)==NULL){
                  CopyStr(temp_path_next,temp_path);
                  AddStr(temp_path_next,"->");
                  AddStr(temp_path_next,NODENAME(Next->A).s);
                 temp_time_next=temp_time;
                 if (type==DOWNLOAD)
                   if (Next->downloadSpeed>0)
                      GraphShortestPathRecursive(size,type,Graph,Next->A,to,out_time,out_shortestpath,temp_time_next+size/(1024*Next->downloadSpeed),temp_path_next,Out);
                 if (type==UPLOAD)
                   if (Next->uploadSpeed>0)
                      GraphShortestPathRecursive(size,type,Graph,Next->A,to,out_time,out_shortestpath,temp_time_next+size/(1024*Next->uploadSpeed),temp_path_next,Out);
            }
       }
       Next=Next->next;
   }
}

void PrintEdge(edgeAddress E){
    char UpParam[100];
    int Down;
    int Count;
    char STR[250];
    
    if (E!=NULL) {
       sprintf(STR,"%s",NODENAME(GetA(E)).s);
       AddSpace(STR,"site");
       printf("%s",STR);
       
       sprintf(STR,"%s",NODENAME(GetB(E)).s);
       AddSpace(STR,"site");
       printf("%s",STR);
       
       sprintf(STR,"%.4f",GetDownload(E));
       AddSpace(STR,"retrieve");
       printf("%s",STR);
       
       sprintf(STR,"%.4f",GetUpload(E));
       AddSpace(STR,"retrieve");
       printf("%s",STR);
       
       printf("\n");
    }                           
}

void	PrintEdgeHeader() {
        char STR[250];
        // SetColor(FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);                                      
        sprintf(STR,"Site-1");
        AddSpace(STR,"site");
        printf("%s",STR);
        
        sprintf(STR,"Site-2");
        AddSpace(STR,"site");
        printf("%s",STR);
        
        sprintf(STR,"Download");
        AddSpace(STR,"retrieve");
        printf("%s",STR);
        
        sprintf(STR,"Upload");
        AddSpace(STR,"retrieve");
        printf("%s",STR); 
        
        printf("\n");
        
        // SetColor(FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
        
}

